﻿namespace DimensionsHelper.Client.WinUi.Helpers;

public static class ScrollHelper
{
    private static bool IsScrollingInternal;


    public static readonly DependencyProperty SynchronizeHorizontalScrollingProperty =
        DependencyProperty.RegisterAttached(
            "SynchronizeHorizontalScrolling",
            typeof(ScrollViewer),
            typeof(ScrollHelper),
            new PropertyMetadata(null, OnSynchronizeHorizontalScrollingPropertyChanged));

    public static readonly DependencyProperty SynchronizeVerticalScrollingProperty =
        DependencyProperty.RegisterAttached(
            "SynchronizeVerticalScrolling",
            typeof(ScrollViewer),
            typeof(ScrollHelper),
            new PropertyMetadata(null, OnSynchronizeVerticalScrollingPropertyChanged));

    private static object SyncRoot { get; } = new();


    public static void SetSynchronizeHorizontalScrolling(DependencyObject d, ScrollViewer value) =>
        d.SetValue(SynchronizeHorizontalScrollingProperty, value);


    public static ScrollViewer? GetSynchronizeHorizontalScrolling(DependencyObject d) =>
        d.GetValue(SynchronizeHorizontalScrollingProperty) as ScrollViewer;


    private static void OnSynchronizeHorizontalScrollingPropertyChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        ScrollViewer source = (ScrollViewer)d;
        ScrollViewer target = (ScrollViewer)e.NewValue;

        source.ViewChanged -= OnSourceScrollViewerScroll;
        source.ViewChanged += OnSourceScrollViewerScroll;

        target.ViewChanged -= OnTargetScrollViewerScroll;
        target.ViewChanged += OnTargetScrollViewerScroll;

        void OnSourceScrollViewerScroll(object? sender, ScrollViewerViewChangedEventArgs e)
        {
            lock (SyncRoot)
            {
                if (!IsScrollingInternal)
                {
                    IsScrollingInternal = true;
                    target.ChangeView(source.HorizontalOffset, null, null, true);
                    IsScrollingInternal = false;
                }
            }
        }

        void OnTargetScrollViewerScroll(object? sender, ScrollViewerViewChangedEventArgs e)
        {
            lock (SyncRoot)
            {
                if (!IsScrollingInternal)
                {
                    IsScrollingInternal = true;
                    source.ChangeView(target.HorizontalOffset, null, null, true);
                    IsScrollingInternal = false;
                }
            }
        }
    }


    public static void SetSynchronizeVerticalScrolling(DependencyObject d, ScrollViewer value) =>
        d.SetValue(SynchronizeVerticalScrollingProperty, value);

    public static ScrollViewer? GetSynchronizeVerticalScrolling(DependencyObject d) =>
        d.GetValue(SynchronizeVerticalScrollingProperty) as ScrollViewer;


    private static void OnSynchronizeVerticalScrollingPropertyChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        ScrollViewer source = (ScrollViewer)d;
        ScrollViewer target = (ScrollViewer)e.NewValue;

        source.ViewChanged -= OnSourceScrollViewerScroll;
        source.ViewChanged += OnSourceScrollViewerScroll;

        target.ViewChanged -= OnTargetScrollViewerScroll;
        target.ViewChanged += OnTargetScrollViewerScroll;

        void OnSourceScrollViewerScroll(object? sender, ScrollViewerViewChangedEventArgs e)
        {
            lock (SyncRoot)
            {
                if (!IsScrollingInternal)
                {
                    IsScrollingInternal = true;
                    target.ChangeView(null, source.VerticalOffset, null, true);
                    IsScrollingInternal = false;
                }
            }
        }

        void OnTargetScrollViewerScroll(object? sender, ScrollViewerViewChangedEventArgs e)
        {
            lock (SyncRoot)
            {
                if (!IsScrollingInternal)
                {
                    IsScrollingInternal = true;
                    source.ChangeView(null, target.VerticalOffset, null, true);
                    IsScrollingInternal = false;
                }
            }
        }
    }
}